
/**
 * @fileoverview A simple charts grid implementation.
 * @link http://google-styleguide.googlecode.com/svn/trunk/javascriptguide.xml
 * @link https://developers.google.com/closure/compiler/docs/js-for-compiler
 */



/**
 * Grid Constructor.
 * @param {string|Element} container The HTML container.
 * @requires formatters.NumberFormatter
 * @constructor
 */
charts.Grid = function(container) {

  /**
   * Draws chart grid.
   * @param {Object=} opt_options A optional grid configuration options.
   */
  this.draw = function(opt_options) {
    options_ = self_.getOptions(opt_options);
    /** @type {number} */ var width = container_.offsetWidth || 200;
    /** @type {number} */ var height = container_.offsetHeight || width;

    height -= options_['grid']['border'] * 2;
    drawLines_(drawCanvas_(width, height), width, height);
  };


  /**
   * Gets grid options merged with defaults grid options.
   * @param {Object=} opt_options A optional grid configuration options.
   * @return {!Object.<string, *>} A map of name/value pairs.
   * @example
   * options: {
   *   'grid': {border': 1, 'color': '#ccc', 'lines': 5},
   *   'font': {'size': 11, 'family': 'Arial'},
   *   'hAxis': true,
   *   'vAxis': true
   * }
   */
  this.getOptions = function(opt_options) {
    opt_options = opt_options || {};
    opt_options['grid'] = opt_options['grid'] || {};
    opt_options['grid']['border'] = opt_options['grid']['border'] || 1;
    opt_options['grid']['color'] = opt_options['grid']['color'] || '#ccc';
    opt_options['grid']['lines'] = opt_options['grid']['lines'] || 5;
    opt_options['font'] = opt_options['font'] || {};
    opt_options['font']['size'] = opt_options['font']['size'] || 11;
    opt_options['font']['family'] = opt_options['font']['family'] || 'Arial';
    opt_options['hAxis'] = 'hAxis' in opt_options ? opt_options['hAxis'] : true;
    opt_options['vAxis'] = 'vAxis' in opt_options ? opt_options['vAxis'] : true;
    return opt_options;
  };

  /**
   * @param {Node} canvas The canvas element.
   * @param {number} width The canvas width.
   * @param {number} height The canvas height.
   * @private
   */
  function drawLines_(canvas, width, height) {
    /** @type {number} */ var minValue = options_['data']['min'];
    /** @type {number} */ var maxValue = options_['data']['max'];
    /** @type {number} */ var padding = options_['padding'] || 0;
    /** @type {number} */ var delta = maxValue - minValue;

    /** @type {number} */ var border = options_['grid']['border'];
    /** @type {string} */ var color = options_['grid']['color'];
    /** @type {number} */ var lines = options_['grid']['lines'];
    /** @type {number} */
    var lineHeight = (height - padding * 2 - border) / (lines - 1) - border;

    // TODO: ???
    setTimeout(function() {container_.style.overflow = 'visible'}, 1);

    for (/** @type {number} */ var i = 0; i < lines + 1; i++) {
      /** @type {Node} */
      var line = canvas.appendChild(dom.createElement('DIV'));
      line.style.overflow = 'hidden';
      if (i && !(!padding && (i == lines || i == 1))) {
        line.style.borderTop = 'solid ' + border + 'px ' + color;
      }

      if (i > 0 && i <= lines) {
        line.style.height = lineHeight + 'px';
        line.style.paddingLeft = '1px';
        if (options_['vAxis']) {
          /** @type {number} */
          var value = parseFloat(minValue + delta / (lines - 1) * (lines - i));
          // TODO: Use formatter instead of:
          var text = delta >= lines - 1 ? parseInt(value, 10) :
                                          value.toFixed(1);
          drawAxisValue_(line, text);
        }
      } else {
        line.style.height = padding + 'px';
      }
    }

    drawXAxisLabels_(canvas, width, height);
  }

  /**
   * @param {Node} canvas The canvas element.
   * @param {number} width The canvas width.
   * @param {number} height The canvas height.
   * @private
   */
  function drawXAxisLabels_(canvas, width, height) {
    if (options_['hAxis'] && options_['data']['columns']) {
      /** @type {Array.<string>} */ var columns = options_['data']['columns'];
      /** @type {number} */ var length = columns.length;
      /** @type {number} */ var padding = (options_['padding'] || 1) * 2;
      /** @type {number} */
      var x = (width - padding * 2) / (length - 2);
      /** @type {number} */
      var size = options_['font'] && options_['font']['size'] || 13;
      /** @type {string} */ var label = getXAxisLabel_(columns[1]);
      /** @type {number} */
      var symbolWidth = label ? label.length * (size - size / 3) : 1;
      /** @type {number} */
      var maxAmount = Math.round((width - padding * 2) / symbolWidth);
      /** @type {number} */
      var step = length / Math.round(maxAmount);
      if (1 > step) step = 1;

      for (/** @type {number} */ var i = 1; i < length;) {
        /** @type {Node} */
        var div = canvas.appendChild(dom.createElement('DIV'));
        div.innerHTML = getXAxisLabel_(columns[i]);
        div.style.position = 'absolute';
        div.style.left = (padding + x * (i - 1) - div.offsetWidth / 2) + 'px';
        div.style.top = (height + 5) + 'px';
        i += Math.round(step);
      }
    }
  }

  /**
   * @param {*} column The column data.
   * @return {string} Returns formatted column label.
   * @private
   */
  function getXAxisLabel_(column) {
    if (column instanceof Date) {
      column = formatters.DateFormatter.format(
          /** @type {Date} */(column), 'YYYY-MM-dd');
    }
    return '' + column;
  }

  /**
   * @param {Node} line The line element.
   * @param {string|number} value The axis value.
   * @private
   */
  function drawAxisValue_(line, value) {
    /** @type {number} */ var x = value.toString().length * 8;
    /** @type {number} */ var y = options_['font']['size'] / 2;
    line.innerHTML = '<div style="position:absolute;z-index:999;' +
        'left:-' + x + 'px;margin-top:-' + y + 'px">' +
        formatter_.round(value) + '</div>';
  }

  /**
   * @param {number} width The canvas width.
   * @param {number} height The canvas height.
   * @return {Node} Returns canvas element.
   * @private
   */
  function drawCanvas_(width, height) {
    /** @type {number} */ var border = options_['grid']['border'];
    /** @type {string} */ var color = options_['grid']['color'];
    /** @type {Node} */
    var canvas = container_.appendChild(dom.createElement('DIV'));
    canvas.style.width = (width - border * 2) + 'px';
    canvas.style.height = height + 'px';
    canvas.style.position = 'absolute';
    canvas.style.top = 0;
    canvas.style.left = 0;
    //canvas.style.zIndex = 0;
    canvas.style.border = 'solid ' + border + 'px ' + color;
    canvas.style.fontFamily = options_['font']['family'];
    canvas.style.fontSize = options_['font']['size'] + 'px';
    canvas.style.color = color;
    return canvas;
  }

  /**
   * The reference to current class instance. Used in private methods.
   * @type {!charts.Grid}
   * @private
   */
  var self_ = this;

  /**
   * The reference to HTML chart container.
   * @type {Element}
   * @private
   */
  var container_ = typeof container == 'string' ?
      dom.getElementById(container) : container;

  /**
   * @dict
   * @private
   */
  var options_ = null;

  /**
   * @type {!formatters.NumberFormatter}
   * @private
   */
  var formatter_ = new formatters.NumberFormatter;
};
